#include "imageinstance.h"

#include "dcmtk/config/osconfig.h"
#include "dcmtk/dcmimgle/dcmimage.h"
#include "dcmtk/dcmdata/dcfilefo.h"
#include "dcmtk/dcmdata/dcdeftag.h"
#include "dcmtk/dcmdata/dcuid.h"
#include "dcmtk/dcmjpeg/djdecode.h"

#include <QFile>

ImageInstance::ImageInstance(const QString &file):
    dcmImage(0),
    pixelX(0),
    pixelY(0),
    imageFile(file)
{
    OFCondition cond = dcmff.loadFile(file.toLocal8Bit().data());
    if (cond.good()) parseImage();
    else err = cond.text();
}

ImageInstance::ImageInstance(DcmFileFormat &dff):
    dcmImage(0),
    pixelX(0),
    pixelY(0)
{
    OFCondition cond = dcmff.copyFrom(dff);
    if (cond.good()) parseImage();
    else err = cond.text();
}

ImageInstance::~ImageInstance()
{
    delete dcmImage;
}

void ImageInstance::parseImage()
{
    DcmDataset *dset = dcmff.getDataset();

    const char *val = 0;
    dset->findAndGetString(DCM_StudyInstanceUID, val);
    studyUid = QString::fromLocal8Bit(val);
    dset->findAndGetString(DCM_SeriesInstanceUID, val);
    seriesUid = QString::fromLocal8Bit(val);
    dset->findAndGetString(DCM_SOPInstanceUID, val);
    imageUid = QString::fromLocal8Bit(val);
    dset->findAndGetString(DCM_SOPClassUID, val);
    classUid = QString::fromLocal8Bit(val);

    dset->findAndGetFloat64(DCM_PixelSpacing, pixelY, 0);
    dset->findAndGetFloat64(DCM_PixelSpacing, pixelX, 1);

    dset->findAndGetFloat64(DCM_WindowWidth, winWidth);
    dset->findAndGetFloat64(DCM_WindowCenter, winCenter);
    defCenter = winCenter;
    defWidth = winWidth;
}

void ImageInstance::initImage()
{
    // Do not call imageOk() to avoid recursive call;
    if (dcmImage || (!isNormal())) return;

    DJDecoderRegistration::registerCodecs();
    //dcmff->loadAllDataIntoMemory();
    DcmDataset *dset = dcmff.getDataset();
    dset->chooseRepresentation(EXS_LittleEndianExplicit, 0);
    dcmImage = new DicomImage(dset, dset->getOriginalXfer());
    EI_Status s = dcmImage->getStatus();
    if (s == EIS_Normal) {
        if (winWidth < 1) {
            dcmImage->setRoiWindow(0, 0, dcmImage->getWidth(), dcmImage->getHeight());
            dcmImage->getWindow(winCenter, winWidth);
            defCenter = winCenter;
            defWidth = winWidth;
        }
    } else {
        delete dcmImage;
        dcmImage = 0;
        DicomImage::getString(s);
    }

    DJDecoderRegistration::cleanup();
}

void ImageInstance::setRoiWindow(const QRectF &rect)
{
    if (imageOk()) {
        dcmImage->setRoiWindow(rect.left(), rect.top(), rect.width(), rect.height());
        dcmImage->getWindow(winCenter, winWidth);
    }
}

void ImageInstance::setFullDynamic()
{
    if (imageOk()) {
        dcmImage->setMinMaxWindow();
        dcmImage->getWindow(winCenter, winWidth);
    }
}

void ImageInstance::setPolarity(EP_Polarity polarity)
{
    if (imageOk()) {
        dcmImage->setPolarity(polarity);
    }
}

EP_Polarity ImageInstance::getPolarity() const
{
    return dcmImage?dcmImage->getPolarity():EPP_Normal;
}

const DicomImage *ImageInstance::getDcmImage() const
{
    return dcmImage;
}

DcmFileFormat *ImageInstance::getFileFormat()
{
    return &dcmff;
}

bool ImageInstance::saveFileFormat()
{
    if (imageOk() && (!imageFile.isEmpty()) &&
        EC_Normal == dcmff.saveFile(imageFile.toLocal8Bit().data())) {
        delete dcmImage;
        dcmImage = 0;
        initImage();
    }
    return false;
}

bool ImageInstance::dcm2bmpHelper(DicomImage &dcmImage, QPixmap &pixmap)
{
    int w = dcmImage.getWidth();
    int h = dcmImage.getHeight();
    void *pDIB = 0;
    int size = dcmImage.createWindowsDIB(pDIB, 0, 0, 32, 0, 1);
    if (size == w * h * 4) {
        QImage image((uchar*)pDIB, w, h, QImage::Format_RGB32, &ImageInstance::freeBuffer, pDIB);
        pixmap = QPixmap::fromImage(image);
        return !pixmap.isNull();
    }
    return false;
}

bool ImageInstance::getPixmap(QPixmap &pixmap)
{
    bool ret = false;

    if (imageOk()) {
        if (winWidth < 1) winWidth = 1;
        dcmImage->setWindow(winCenter, winWidth);
        ret = dcm2bmpHelper(*dcmImage, pixmap);
    }

    return ret;
}

bool ImageInstance::getPixmap(const QString &dicomFile, QPixmap &pixmap)
{
    ImageInstance image(dicomFile);
    return image.getPixmap(pixmap);
}

DicomImage* ImageInstance::createClippedImage(const QRect &rect, int angle, bool hflip, bool vflip, bool inverted)
{
    if (!imageOk()) return 0;

    int ret = 1;
    double min, max;
    dcmImage->getMinMaxValues(min, max);
    Uint16 pvalue = dcmImage->getPhotometricInterpretation()==EPI_Monochrome1?max:min;
    DicomImage *newImage = dcmImage->createClippedImage(rect.left(), rect.top(), rect.width(), rect.height(), pvalue);
    if (newImage) {
        if (ret && angle) ret = newImage->rotateImage(angle%360);
        if (ret && hflip) ret = newImage->flipImage(1, 0);
        if (ret && vflip) ret = newImage->flipImage(0, 1);
        if (ret && inverted) ret = newImage->setPolarity(EPP_Reverse);
        if (!ret) {
            delete newImage;
            newImage = 0;
        }
    }
    return newImage;
}

QString ImageInstance::getTagKeyValue(const DcmTagKey &key)
{
    OFString val;
    dcmff.getDataset()->findAndGetOFString(key, val);
    return QString::fromLocal8Bit(val.c_str());
}

double ImageInstance::getPixelValue(long x, long y) const
{
    if (dcmImage) {
        const DiPixel* pixel = dcmImage->getInterData();
        long w = dcmImage->getWidth();
        long h = dcmImage->getHeight();
        if (pixel && (x < w) && (x >= 0)
                && (y < h) && (y >= 0)) {
            EP_Representation r = pixel->getRepresentation();
            switch (r) {
            case EPR_Sint8:
                return *((char*)(pixel->getData()) + (y * w + x));
            case EPR_Uint8:
                return *((uchar*)(pixel->getData()) + (y * w + x));
            case EPR_Sint16:
                return *((short*)(pixel->getData()) + (y * w + x));
            case EPR_Uint16:
                return *((ushort*)(pixel->getData()) + (y * w + x));
            case EPR_Sint32:
                return *((int*)(pixel->getData()) + (y * w + x));
            case EPR_Uint32:
                return *((uint*)(pixel->getData()) + (y * w + x));
            }
        }
    }

    return 0;
}

bool ImageInstance::getPixSpacing(double &spacingX, double &spacingY) const
{
    if (isNormal()) {
        spacingX = pixelX;
        spacingY = pixelY;
        return true;
    }
    return false;
}

bool ImageInstance::getImageSize(ulong &width, ulong &height)
{
    DcmDataset *dset = dcmff.getDataset();
    OFCondition cond1, cond2;
    ushort w, h;
    cond1 = dset->findAndGetUint16(DCM_Columns, w);
    cond2 = dset->findAndGetUint16(DCM_Rows, h);
    if (cond1.good() && cond2.good()) {
        width = w;
        height = h;
        return true;
    }
    return false;
}

const short *ImageInstance::getInternalPtr() const
{
    return dcmImage?(const short*)dcmImage->getInterData()->getData():0;
}

const ushort *ImageInstance::getRawData()
{
    if (isNormal()) {
        const ushort *data = 0;
        OFCondition cond = dcmff.getDataset()->findAndGetUint16Array(DCM_PixelData, data);
        return cond.bad()?0:data;
    }
    return 0;
}
